﻿/********
* 
* 
*  Description:
*  Create By kain.hong at 8/4/2010 10:32:09 AM
*  
*
*  Revision History:
*  Date                  Who                 What
*  
* 
*/
#region namespace

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using Cherry.Agile.ServiceModel.Interceptor;
using System.Runtime.Serialization;
using Cherry.Agile.ServiceModel.Runtime;
using Cherry.Agile.Core;
using Microsoft.Practices.Unity;
using Cherry.Agile.ServiceModel.Runtime.Instrumentation;
using System.Collections.Concurrent;
using System.ServiceModel.Configuration;
#endregion


namespace Cherry.Agile.ServiceModel {
    /// <summary>
    /// WCF Client 上下文管理
    /// </summary>
    public sealed class ClientFactory : IDisposable {
        //Dictionary<RuntimeTypeHandle, IClientChannel> _innerDict = new Dictionary<RuntimeTypeHandle, IClientChannel>();

        private static ClientFactory _current = null;
        ConcurrentDictionary<int, object> _channelFactories = new ConcurrentDictionary<int, object>();
        public ClientFactory() {
            _current = this;
        }

        /// <summary>
        /// 获取或者创建一个通道，该通道用于将消息发送到以指定方式配置其终结点的服务
        /// </summary>
        /// <typeparam name="T">通道类型</typeparam>
        /// <returns>工厂创建的 IChannel 类型的 TChannel</returns>
        public T CreateClient<T>() {
            if (!typeof(T).IsInterface)
                throw new ArgumentException("T 必须为接口");
            var handler = typeof(T).TypeHandle;

            IClientChannel result;
            var factory = new ChannelFactory<T>();
            result = CreateChannel<T>(factory);
            return (T)result;
        }

        /// <summary>
        /// 获取或者创建一个通道，该通道用于将消息发送到以指定方式配置其终结点的服务
        /// </summary>
        /// <typeparam name="T">通道类型</typeparam>
        /// <param name="binding">为由工厂生成的通道指定的 <see cref="System.ServiceModel.Channels.Binding"/></param>
        /// <param name="remoteAddress">提供服务位置的 <see cref="EndpointAddress"/></param>
        /// <param name="isBidirectional">是否支持双向消息绑定</param>
        /// <returns>工厂创建的 IChannel 类型的 TChannel</returns>
        public T CreateClient<T>(Binding binding, EndpointAddress remoteAddress, bool isBidirectional) {
#if DEBUG
            if (!typeof(T).IsInterface)
                throw new ArgumentException("T 必须为接口");
#endif
            var handler = typeof(T).TypeHandle;

            IClientChannel result;
            var factory = GetFacotry<T>(binding, remoteAddress, isBidirectional);
            result = CreateChannel<T>(factory, isBidirectional);

            return (T)result;
        }

        /// <summary>
        /// 获取或者创建一个通道，该通道用于将消息发送到以指定方式配置其终结点的服务
        /// </summary>
        /// <typeparam name="T">通道类型</typeparam>
        /// <param name="binding">为由工厂生成的通道指定的 <see cref="System.ServiceModel.Channels.Binding"/></param>
        /// <param name="remoteAddress">提供服务位置的地址</param>
        /// <returns>工厂创建的 IChannel 类型的 TChannel</returns>
        public T CreateClient<T>(Binding binding, string remoteAddress) {

            return CreateClient<T>(binding, new EndpointAddress(remoteAddress), true);
        }

        /// <summary>
        /// 获取或者创建一个通道，该通道用于将消息发送到以指定方式配置其终结点的服务
        /// </summary>
        /// <typeparam name="T">通道类型</typeparam>
        /// <param name="endpointConfigurationName">用于终结点的配置名称</param>
        /// <param name="remoteAddress">提供服务位置的 <see cref="EndpointAddress"/></param>
        /// <returns>工厂创建的 IChannel 类型的 TChannel</returns>
        public T CreateClient<T>(string endpointConfigurationName, EndpointAddress remoteAddress) {
            if (!typeof(T).IsInterface)
                throw new ArgumentException("T 必须为接口");
            var handler = typeof(T).TypeHandle;
            IClientChannel result;

            var factory = GetFacotry<T>(endpointConfigurationName, remoteAddress, true);
            result = CreateChannel<T>(factory, false);
            return (T)result;
        }

        /// <summary>
        /// 获取或者创建一个通道，该通道用于将消息发送到以指定方式配置其终结点的服务
        /// </summary>
        /// <typeparam name="T">通道类型</typeparam>
        /// <param name="endpointConfigurationName">用于终结点的配置名称</param>
        /// <returns>工厂创建的 IChannel 类型的 TChannel</returns>
        public T CreateClient<T>(string endpointConfigurationName) {
            return CreateClient<T>(endpointConfigurationName, null);
        }

        private ChannelFactory<T> GetFacotry<T>(Binding binding, EndpointAddress remoteAddress, bool isBidirectional) {
            var key = (typeof(T).FullName + "," + binding.GetType().Name
                    + "," + (remoteAddress != null ? remoteAddress.Uri.ToString() : string.Empty)
                    + "," + isBidirectional.ToString()).GetHashCode();

            object val;
            if (_channelFactories.TryGetValue(key, out val))
                return val as ChannelFactory<T>;
            ChannelFactory<T> factory = SetupCustomerBehaviors(new ChannelFactory<T>(binding, remoteAddress), isBidirectional);
            _channelFactories.TryAdd(key, factory);
            return factory;
        }

        private ChannelFactory<T> GetFacotry<T>(string endpointConfigurationName, EndpointAddress remoteAddress, bool isBidirectional) {
            var key = (typeof(T).FullName + "," + endpointConfigurationName
                + "," + (remoteAddress != null ? remoteAddress.Uri.ToString() : string.Empty)
                + "," + isBidirectional.ToString()).GetHashCode();
            object val;
            if (_channelFactories.TryGetValue(key, out val))
                return val as ChannelFactory<T>;
            ChannelFactory<T> factory = SetupCustomerBehaviors(new ChannelFactory<T>(endpointConfigurationName, remoteAddress), isBidirectional);
            _channelFactories.TryAdd(key, factory);
            return factory;
        }

        private ChannelFactory<T> GetFacotry<T>(bool isBidirectional) {
            var key = (typeof(T).FullName + "," + isBidirectional.ToString()).GetHashCode();
            object val;
            if (_channelFactories.TryGetValue(key, out val))
                return val as ChannelFactory<T>;
            var factory = SetupCustomerBehaviors(new ChannelFactory<T>(), isBidirectional);
            _channelFactories.TryAdd(key, factory);
            return factory;
        }

        private ChannelFactory<T> SetupCustomerBehaviors<T>(ChannelFactory<T> factory, bool isBidirectional = true) {
            var endpoint = factory.Endpoint;
            endpoint.Behaviors.Add(new EndpointInterceptorAttribute(isBidirectional));
            ApplyDefaultBehaviors(factory);
            return factory;
        }

        private void ApplyDefaultBehaviors(ChannelFactory factory) {
            var section = (System.ServiceModel.Configuration.BehaviorsSection)System.Configuration.ConfigurationManager.GetSection("system.serviceModel/behaviors");
            foreach (EndpointBehaviorElement item in section.EndpointBehaviors) {
                if (!string.IsNullOrEmpty(item.Name))
                    continue;
                foreach (BehaviorExtensionElement be in item) {
                    if (factory.Endpoint.Behaviors.Contains(be.BehaviorType))
                        continue;
                    var obj = Activator.CreateInstance(be.BehaviorType) as IEndpointBehavior;
                    factory.Endpoint.Behaviors.Add(obj);
                }
            } 
        }

        private IClientChannel CreateChannel<T>(ChannelFactory<T> factory, bool isBidirectional = true) {
            return factory.CreateChannel() as IClientChannel;
        }

        /// <summary>
        /// 当前Scope的实例
        /// </summary>
        public static ClientFactory Current {
            get {
                if (_current == null) {
                    _current = new ClientFactory();
                }
                return _current;
            }
        }

        public void Dispose() {
            //foreach (var item in _innerDict.Values)
            //{
            //    try
            //    {
            //        if (item.State == CommunicationState.Faulted)
            //        {
            //            item.Abort();
            //            if (_instrumentation != null)
            //                _instrumentation.Faulted();
            //        }
            //        else
            //        {
            //            item.Close();
            //            if (_instrumentation != null)
            //                _instrumentation.Closed();
            //        }
            //    }
            //    catch
            //    {

            //    }
            //    item.Dispose();
            //}
            if (_current == this)
                _current = null;
        }
    }

}
